#ifndef DATABASE_LISTOBJECT_H
#define DATABASE_LISTOBJECT_H

#include "Object.hpp"
#include "../Structurt/Linker.hpp"

#include <unordered_map>
#include <vector>
#include <functional>

namespace DBServer
{
namespace Database
{
    
class ListObject : public Object {
public:
    using Callback = std::function<void(ListObject&, CommandRequest&&, CommandRespond&)>;

    ListObject()
    :Object(LIST_TYPE)
    {}
    ~ListObject() = default;

    void command_excute(CommandRespond&, CommandRequest&&, com_func_t&);

public:
    typedef Linker<std::string> Linker_;

    /**
     * the command of ListObject:
     * 1.LPUSH      KEY     VALUE
     *      add a element from the head of the ListObject
     * 2.RPUSH      KEY     VALUE
     *      add a elememt from the tail of teh ListObject
     * 3.LPOP       KEY
     *      remove the head of the ListObject
     * 4.RPOP       KEY
     *      remove the tail of the ListObject
     * 5.LINDEX     KEY     VALUE
     *      remove the element on the specified element of the ListObject
     * 6.LLEN       KEY
     *      get the number of elements in the ListObject
     * 7.LINSERT    KEY     VALUE1   VALUE2
     *      add a element at the specified index of the ListObject,
     *      if the index is 0 or the index is the size of ListObject,
     *      this command is equal to LPUSH or RPUSH.
     * 8.LREMOVE    KEY     VALUE
     *      remove the element which is on the specified index of the ListObject
     * 9.LSET       KEY     VALUE1   VALUE2
     *      reset the elemetn which is on the specified index of the ListObjec
     * 10.LTRIM     KEY     VALUE1   VALUE2
     *      remove the elements which are in the range of [VALUE1, VALUE2)
     *      if VALUE2 - VALUE1 == 1, this command is equal to LREMOVE
     * 11.GET       KEY
     *      get all element in the object
     * 
    */
    inline auto lpush(std::string&& ele) -> int
    {
        linker.push_front(ele);
        return linker.size();
    }

    inline auto rpush(std::string&& ele) -> int
    {
        linker.push_back(ele);
        return linker.size();
    }

    inline auto lpop() -> std::string
    {
        auto pop_ele = linker.get_head().data;
        // get the specified data and then delete it
        linker.erase(linker.get_head().data);
        return pop_ele;
    }

    inline auto rpop() -> std::string
    {
        auto pop_ele = linker.get_tail().data;
        // get the specified data and the delete it
        linker.erase(linker.get_tail().data);
        return pop_ele;
    }

    inline auto lindex(int index, std::string& ret) -> bool
    {
        if (linker.size() <= index || index < 0)
        {
            return false;
        }
        auto iter = linker.get(index);
        // assignment the value
        ret = iter->node->data;
        return true;
    }

    inline auto llen() -> int
    {
        return linker.size();
    }

    inline auto linsert(int index, std::string&& new_value) -> bool
    {
        if (index >= linker.size() || index < 0) 
        {
            return false;
        }
        // get iterator
        auto iter = linker.get(index);
        linker.iter_insert(iter, new_value);
        return true;
    }

    inline auto lremove(int index, std::string& remove_ele) -> bool
    {
        if (index >= linker.size() || index < 0)
        {
            return false;
        }

        // get iterator
        auto iter = linker.get(index);
        remove_ele = iter->node->data;
        linker.erase(iter);

        return true;
    }

    inline auto lset(int index, std::string&& new_value) -> bool
    {
        if (index >= linker.size() || index < 0)
        {
            return false;
        }

        // get iterator
        auto iter = linker.get(index);
        // reset the data of the node
        iter->node->data = new_value;

        return true;
    }

    inline auto ltrim(int begin, int end) -> int
    {
        if (begin >= linker.size() || end < 0)
        {
            return 0;
        }
        // ensure the range
        begin = (begin > 0) ? begin : 0;
        end = (end >= linker.size()) ? linker.size()-1 : end;

        // get iter
        auto iter_front = linker.get(begin);
        auto iter_end = linker.get(end);
        auto iter_erase = iter_front;

        // remove
        int counter = 0;
        while (iter_front != iter_end)
        {
            iter_erase = iter_front;
            ++iter_front;
            ++counter;
            linker.erase(iter_erase);
        }
        return counter;
    }

    inline auto get() -> std::vector<std::string>
    {
        std::vector<std::string> tmp;

        auto iter = linker.get_iterator();
        while (iter != linker.end())
        {
            tmp.push_back(iter->node->data);
            ++iter;  
        }
        
        return tmp;
    }

private:
    Linker_ linker;
    static std::unordered_map<int, Callback> cb_table;
};

} // namespace Database
} // namespace DBServer


#endif